Numpy & Pandas
まじで毎回忘れる
統計量出す
df.describe()
df.describe(exclude='number') でカテゴリデータ
Series も describe ある & datetime なら first / last も出る
思考停止で使える
カラム名や型など情報見る
df.info()
欠損値探す
df.isnull().sum()
df.info() でもだいたい出る
df[df.isnull().any(axis=1)] 1つでも欠損値がある行を取り出す
df[~df.isnull().any(axis=1)] 1つでも欠損値を含む行を除く
最初と最後の5行出す
head, tail 使う他に print(df, 5) もある
ある列の最大値を持つ行
df.loc[[df['col'].idxmax()]]
idxmax で行番号を得て loc で取得
テーブルの加工
df['col'] -> Series 特定の名前の列をとりだす
df[['col1', 'col2']] -> DataFrame 列選択して DataFrame に(1列でもよい)
df.drop(['col1', 'col2'], axis=1) -> DataFrame 列指定して捨てる
df.drop(df.tail(1).index, inplace=True) 末尾1行捨てる、合計とかが入ってて邪魔な時に
a.columns = ['_'.join(filter(None, map(str, col))).strip() if len(col) > 1 else str(col[0]) for col in a.columns.values]
マルチレベルの列が混ざっている場合に、_ で結合しつつフラットにする
groupby して agg で1つのカラムの min と max を出したりすると起きる
df.melt(id_vars, value_vars, var_name, value_name)
id_vars を列として維持し
value_vars 列を var_name 列として縦持ちにする
値を value_name 列にする
毎回どうだっけ? てなるので絵にした
https://gyazo.com/03b0432b68bcf25823d13c31c0389abe
melt よりイメージはつきやすい
df.pivot(index, columns, values)
上の絵の逆の操作をやるのが
df.pivot(index=['ID', 'Region'], columns='Quarter', values='Sales')
DataFrame
複製
df.copy()
条件に合う行を取り出す
df[df['column'] > 1000]
df.query('column > 1000') 遅いのでチェーンで書きたいときだけ
Bool で比較するときは is ではなく ==
df[df['label'].isin(['hoge', 'fuga'])]
isin で特定の値のものにしぼる
否定 IS NOT IN は ~Series になる
df[~df['label'].isin(['hoge', 'fuga'])]
df[0:1] などのスライスで行番号で参照
df[df['name'].str.contains('hoge')]
文字列として含む
contains('hoge', na=False) で空なら除ける
datetime 文字列比較でなんかいける
df[df['timestamp'] >= '2022-08-01']
datetime と比較してもいい
df[df[date] > datetime(2023, 6, 29)]
カッコでくくって & や | で複数条件
df[(df['name'] == 'pokutuna') | (df['name'] == 'oneetyan)]
列を取り出す
df['column_name'] -> Series
df.pop('col') で削除しつつ取り出す、目的変数分ける時などに
並び替え
df.sort_values('colmun', ascending=False)
ascending=False で降順
横に結合
df['append_column'] = series
index フリ直し
sort したあとなど index がバラバラになる
df.reset_index(drop=True) で index を振り直し元の index を消す
json のパース
pd.json_normalize() を使う
入力は json じゃなくて dict でよい
data には list か pd.Series を期待している
BigQuery & Cloud Logging などで jsonPayload を展開して元の DataFrame にくっつけたい
code:normalize_concat.py
payload = pd.json_normalize(df'jsonPayload').add_prefix('jsonPayload.') record_prefix='jsonPayload.' してもうまくいかない
タイムゾーン
変換するなら tz_convert
セットするなら tz_localize?
時刻のパース
df['parsed'] = pd.to_datetime(df['year'], format="%Y")
df['dt'] = pd.to_datetime(df['timestamp']).dt.tz_convert('Asia/Tokyo')
format
20230614 みたいなパースは %Y%m%d
23/01/02 は %y/%m/%d
pd.load_csv や pd.load_table で読む
table のほうは区切り文字が \t
列名、 header=None や names=('A', 'B')
BigQuery から timestamp & jsonPayload を SELECT してきてパース
code:format.py
payload = pd.json_normalize(df'jsonPayload').add_prefix('jsonPayload.') df
複数の Series を prefix で選択
df.filter(regex='^prefix',axis=1) で、 prefix から始まる Series を集めた df が返る
datetime を index にする
df["date"] = pd.to_datetime(df["date"])
df.set_index("date", inplace=True)
df['2023'], df['2022-01':'2022-12'] のような指定で部分を取り出せる
こういう問題もある
連番振る
df['num'] = range(0, len(df.index))
転置
df.T
各列の平均や標準偏差の Series を取る時などに
df.describe().T['mean']
Series
series.describe() とりあえず
series.mean()
series.value_counts() 値ごとにカウントする, dropna=False で NaN をカウントできる
series.append(series)
別の series をくっつける
s1.adppend(pd.Series([1,2,3], index=['foo', 'bar', 'baz']))
series.dtype で型を見る
series.isin(['foo']).any()
series のなかに 'foo' を含むかどうか
isin は Series の各値に対して含むかどうかチェックする、True/False の series ができるので any でいずれかが True であることを調べる、all もある
series.str.split('_').str.get(0)
文字列として _ で切って 0 番目のものだけ
GroupBy
groupby で返るのは DataFrameGroupBy や SeriesGroupBy オブジェクト
メソッドを呼ぶとグループごとに適用された結果が返る
df.groupby(['a', 'b']) で複数のカラムでグループ化
df.groupby(..., as_index=False) groupby したカラムの値を index にしない
表を操作しているイメージや Altair で使う時は index じゃないほうがいい 最後に reset_index() してもよい
グループに対してチェーンして集計したい時はそうなる
df.groupby(...).first() 各グループの最初の値を返す
df.groupby(...).agg({'col1': np.sum, 'col2': ['sum', 'count'] })
group に対して agg で集計を行う
関数名を渡せる
count: 行のカウント
nunique: ユニークカウント
lambda を渡せる
df.groupby(...).agg({'col1': lambda x: (x==True).sum()}
col1 が True のものをカウント
各グループのサイズを得るには .size()
df.groupby(...).size()
df.groupby(...).apply(...)
apply の lambda が何返すかによって結果全然異なる?
DataFrame / Series / スカラー
スカラーを Series にして名前を付けて返すと新しい列の名前を指定しつつ集計できる
code:yokuyaru.py
df.groupby([
pd.Grouper(key="timestamp", freq="D"),
"subgroup",
]).apply(lambda x: pd.Series(
{"new_col": x"a".str().contains().count() /x"b".count() } )).reset_index()
グループ化してカウントして表にする
data.groupby(['HomePlanet','Destination','Solo','Cabin_deck'])['Cabin_deck'].size().unstack().fillna(0)
groupby に渡す
df.groupby(pd.Grouper(key="dt", freq="D"), as_index=False).size()
df.groupby(pd.Grouper(key="date", freq="D")).sum()
(時以降も入っている) datetime のまま日単位でグループ化する
複数渡せる
df.groupby([pd.Grouper(key="dt", freq="D"), pd.Grouper(key="is_user")])
resample でも似たようなことができる、grouper のほうが明確で好みかなあ
df.resample('30T', on='dt').sum()
単にユニークにしたい場合は drop_duplicates
Binning
cut, qcut を使う
bin 用のSeries を作る
df['bin'] = df[['rank']].apply(pd.cut, bins=list(range(0, 1100, 100)))
順位が入った Series の値を binning する
加工
map
df['mapped'] = df['col']
速度は map > apply >>> for ループ?
時刻のパース
df['dt'] = pd.to_datetime(df['timestamp'])
in-place に変換する方法はないのかな?
json のパース
pd.json_normalize(df['json'])
obj.field というカラムになる
展開して横につなげるには
pd.concat([df, pd.json_normalize(df['json'])], axis='columns')
区切り文字を . から変えたり、prefix を付けたり
pd.json_normalize(df['ua'], sep="_").add_prefix('ua_')
特定の区間の値に制限する
df.clip(None, 1.0, inplace=True)
繰り返す
np.tile([1,2,3], 3) #=> [1,2,3,1,2,3,1,2,3]
移動平均
df.rolling(7).mean()
週ごと・月ごと
df.resample("W").sum().plot()
M ならmonth
30T なら 30分
型の変換
たまに df.dtypes、df['col'].dtype で確認する
あるあるかつハマるのでダルい
np.NaN は float なので、int64 の列に NaN 込みのデータを結合すると float64 になる
pd.Series([np.NaN]).dtype # => dtype('float64')
bool を 0, 1 に
code:convert_bool.py
def convert_bool(in_df):
df = in_df.copy()
for col in df.columns:
if dfcol.dtype == 'bool': return df
実際には bool だけじゃなく True / False / NaN だったりする
seaborn の pairplot などで bool 避けたいだけなら 1, 0, -1 などに replace する df[col].replace({True: 1, False: 0, np.nan: -1})
plot
styler
df.style.highlight_max
TODO style.pipeでやる
npz
numpy バイナリフォーマット
with np.load(path) as data: ...
data.files で列見れる
progress_apply
プログレスバーを表示しながら apply できる
code:tqdm.py
from tqdm import tqdm
tqdm.pandas()
np.ndarray の Series を np.ndarray にネストさせる
何言っているのか
既に個々のセルの値が np.array なので、to_numby だと
np.array([np.array([...]), np.array([...], ...]) になる、np.stack するとよい
code:stack.py
series = pd.Series([
])
# を
np.array(1,2,3], 4,5,6, [7,8,9) # にしたい
np.array(series.tolist())
# もしくは
np.stack(series.to_numpy())